/*
** TASK_UTILITIES.C
**
** Utility functions to use with pRec/pTask 
**		structures & routines
*/

#include <stddef.h>
#include "toys.h"
#include "task_utilities.h"
#include "property_list.h"

/* PUBLIC ROUTINES */

	/* make trials */

/* PUBLIC ROUTINE: tu_make_trials1D
**
*/
void tu_make_trials1D(_PRtask task, 
	char *name, int num, int *values, float multiplier)
{
	register int i;

	/* check args ... be sure to free trials
	** if none given
	*/
	if(num <= 0) {
		pr_task_clear(task);
		return;
	}

	/* add the array of (empty) trials */
	pr_task_add_trials1D(task, num);

	/* For each, add a list with the value
	** of the given property.
	*/
	for(i=0;i<num;i++) {
		task->trials[i]->id = task->id*100+i;
		task->trials[i]->list = 
			pl_list_initV("1dtc", 0, 1,
				name, values[i], multiplier,
				NULL);
	}
}

/* PUBLIC ROUTINE: tu_make_trials2D
**
**	Utility to make a 2D trial set.
**	Parameter #1 is rows
**	Parameter #2 is columns
**
**	Remember that, as per C conventions, matrix
**		is stored row by row.
**
**	Arguments:
**		_PRtask ... the task to update
**		name1	  ... string name of paramter #1
**		n1	  	  ... number of values in paramter #1 array (rows)
**		v1	  	  ... array of values for paramter #1
**		m1		  ... multiplier for paramter #1
**		name2	  ... string name of paramter #2
**		n2	  	  ... number of values in paramter #2 array (cols)
**		v2	  	  ... array of values for paramter #2
**		m2		  ... multiplier for paramter #2
*/

void tu_make_trials2D(_PRtask task, 
	char *name1, int n1, int *v1, float m1,
	char *name2, int n2, int *v2, float m2)
{
	long dummy = 0;
	register int i, j, k;

	/* check args ... be sure to free trials
	** if none given
	*/
	if(n1 <= 0 && n2 <= 0) {
		pr_task_clear(task);
		return;
	}

	if(n1 <= 0) {
		n1 = 1;
		v1 = &dummy;
		m1 = 1.0;
	}
	if(n2 <= 0) {
		n2 = 1;
		v2 = &dummy;
		m2 = 1.0;
	}

	/* add the array of (empty) trials */
	pr_task_add_trials2D(task, n1, n2);

	/* For each, add a list of 2 properties,
	**	corresponding to the values of the two
	**	variables.
	*/
	for(k=0,i=0;i<n1;i++) {
		for(j=0;j<n2;j++,k++) {
			task->trials[k]->id = task->id*100+k;
			task->trials[k]->list = 
				pl_list_initV("2dtc", 0, 1,
					name1, v1[i], m1,
					name2, v2[j], m2, 
					NULL);
		}
	}
}

	/* get trials */

/* PUBLIC ROUTINE: tu_get_block
**
**	Gets the next trial from a standard (not randomized) block,
**		making the block if necessary
**
**	Arguments:
**		task 			... See paradgim_rec for details.
**		num_blocks 	... Number of blocks (reps) of the set of trials
**								that should be used when creating the 
**								pointer array.
**		reset_flag  ... If true, set the pointer back to the beginning
**								after reaching the end of the array.
**		
*/
_PRtrial tu_get_block(_PRtask task, int num_blocks, int reset_flag)
{
	int last_score = task->pmf ? task->pmf->last_score : 0;

	/* Conditionally make a single block...
	** This will also reset the index to 0
	*/
	if(task->trialPs == NULL) {

		pr_task_make_trialP_all(task, num_blocks);

		/* check that it worked */
		if(task->trialPs == NULL)
			return(NULL);

	/* Otherwise increment the pointer and check for
	** end of the array
	*/
	} else if(last_score >= 0 &&
		++(task->trialPs_index) >= task->trialPs_length) {

		/* end of the line ... check for reset */
		if(reset_flag > 0)

			/* reset pointer back to the beginning of the array */	
			task->trialPs_index = 0;

		else

			/* no reset, end of the line */
			return(NULL);
	}

	/* return the current trial */
	return(task->trialPs[task->trialPs_index]);
}

/* PUBLIC ROUTINE: tu_get_random
**
**	Gets the next trial in a randomized block,
**		making the block if necessary.
**
** Arguments:
**		task 			... See paradgim_rec for details.
**		num_blocks 	... Number of blocks (reps) of the set of trials
**								that should be used when creating the 
**								pointer array.
**		reset_flag  ... If true, set the pointer back to the beginning
**								after reaching the end of the array.
*/
_PRtrial tu_get_random(_PRtask task, int num_blocks, int reset_flag)
{	
	int last_score = task->pmf ? task->pmf->last_score : 0;

	if(task->trialPs) {

		/* check previous score ... */
		if(last_score < 0) {

			/* swap trial with later trial */
			pr_task_swap_trialP(task);

		/* try to increment the pointer */
		} else if((++(task->trialPs_index)) == task->trialPs_length) {

			/* reached the end, clear pointer array
			** so next time through it gets re-randomized
			*/
			SAFE_FREE(task->trialPs);
			task->trialPs_length = 0;
		}

	} else {
		
		/* force 'reset' first time through */
		reset_flag = 1;
	}

	/* check to make block */
	if(reset_flag && !task->trialPs) {

		/* make the randomized set... the second argument
		** determines the minimum number of trial
		** blocks to include in the array -- this
		** is to make sure that redoing trials
		** after an nc/brfix doesn't cause
		** a predictable pattern to accumulate
		** at the end of the list
		*/
		pr_task_make_trialP_allR(task, num_blocks);
	}

	/* check if there is a list of pointers */
	if(!task->trialPs)
		return(NULL);

	/* return pointer to the current trial */
	return(task->trialPs[task->trialPs_index]);
}

/* PUBLIC ROUTINE: tu_get_stair
**
*/
_PRtrial tu_get_stair(_PRtask task, int num_blocks, int reset_flag)
{
	printf("STAIRCASE!!!!\n");
}

/* PUBLIC ROUTINE: tu_compute_xyrt
**
**	Computes x,y positions of a set of
**		graphics objects with properties 
**		defined as "TU_XYRT_LIST"  
**		(see task_utilities.h)
**
*/
void tu_compute_xyrt(_PLlist list)
{
	int pi=0;
	register int i=0;

	if(!list || !list->properties_length || 
		!list->values_length)
		return;

	/* find index of property DX_X */
	while(pi<list->properties_length && 	
		strcmp(list->properties[pi]->name, DX_X)) pi++;
	if(pi == list->properties_length)
		return;

	/* set each rt */
	for(i=0;i<list->values_length;i++) {
		tu_compute_rtR(list->properties, pi, i);

/*
**		printf("obj %d, r = %d, t = %d\n", i, 
**			list->properties[pi+kR]->values_wc[i],
**			list->properties[pi+kT]->values_wc[i]);
*/
	}

	/* set each xy */
	for(i=0;i<list->values_length;i++) {
		tu_compute_xyR(list->properties, pi, i);

/*
**		printf("obj %d, x = %d, y = %d\n", i, 
**			list->properties[pi]->values_wc[i],
**			list->properties[pi+1]->values_wc[i]);
*/
	}
}

#define WVI(ki_,vi_) 	ps[pi+(ki_)]->values_wc[(vi_)]
#define WV(ki_)  			WVI(ki_, vi)
#define WC_VDV(v_,dv_) 	WC_V(v_, dv_); WC_DV(v_,dv_)
#define WC_V(v_,dv_)  	if(WV(v_) == NULLI) { WV(v_) = 0; }
#define WC_DV(v_,dv_) 	if(WV(dv_)>0) { 							\
					WV(v_) += (TOY_RAND(2*WV(dv_)+1) - WV(dv_)); \
					WV(dv_) = NULLI; }

void tu_compute_rtR(_PLproperty *ps, int pi, int vi)
{
	int wrti;

	/* 
	** Check r,t
	*/
	WC_VDV(kR, kDR);
	WC_VDV(kT, kDT);

	/* 
	** Get WRT angle ... first get index of wrt object,
	**		then marks as NULLI to avoid inf recurs.
	*/
	wrti 		= WV(kWRT);
	WV(kWRT) = NULLI;

	/* check for no wrt */
	if(wrti == NULLI || wrti == vi || wrti < 0 || 
		wrti >= (*ps)->values_length)
		return;

	/* recursively compute the angle ! */
	tu_compute_rtR(ps, pi, wrti);	

	/* add the angle */
	WV(kR) += WVI(kR, wrti);
}

void tu_compute_xyR(_PLproperty *ps, int pi, int vi)
{
	int vertexi, vx, vy;

	/* 
	** Check if x, y are set ...
	*/
	if(WV(kX) != NULLI && WV(kY) != NULLI) {

		/* check for dx, dy */
		WC_DV(kX, kDX);
		WC_DV(kY, kDY);

		/* outta */
		return;
	}

	/* r, t should already be set */
	vertexi 		= WV(kVERTEX);
	WV(kVERTEX) = NULLI;

	/* check for no vertex */
	if(vertexi == NULLI || vertexi == vi || vertexi < 0 ||
		vertexi >= (*ps)->values_length) {

		vx = 0;
		vy = 0;

	} else {

		tu_compute_xyR(ps, pi, vertexi);

		vx = WVI(kX, vertexi);
		vy = WVI(kY, vertexi);
	}

	/* compute x, y from r, t, and 
	**		add dx,dy if neccesary.
	*/
	WV(kX) = TOY_RT_TO_X(vx, WV(kR), WV(kT));
	WC_DV(kX, kDX);

	WV(kY) = TOY_RT_TO_Y(vy, WV(kR), WV(kT));
	WC_DV(kY, kDY);

/*
**	printf("vt=%d,vx=%d,vy=%d,kr=%d,kt=%d,kdx=%d,kdy=%d,kx=%d,ky=%d\n",
**		vertexi,vx,vy,WV(kR),WV(kT),WV(kDX),WV(kDY),WV(kX),WV(kY));
*/
}
